﻿#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <Windows.h>
#define automalloc(x) (x*)malloc(sizeof(x))//宏定义一个便于动态申请链表
//三种分割线
#define HEAD "========================================================================================="
#define HEAD2 "| 序号  |          姓名          |        电话号码         |            备注            |"
#define HEAD3 "-----------------------------------------------------------------------------------------"
//宏定义输出
#define print(cnt, name, number, remark) printf("|%-7d|%-24s|%-25s|%-28s|\n", cnt, name, number, remark)

struct stu {
	char name[30] = { 0 };
	char number[30] = { 0 };
	char remark[30] = { 0 };
	stu* next = NULL, *last = NULL;
};

stu* Student = automalloc(stu);

void hello();//各种函数声明
void display_menu();
void Init();
void Save();
void Add();
void Search();
void Delete();
void Modify();
void Display();
void swap(stu*, stu*);
void Sort();
void main_menu();
void Author();
void bye();

int main() {//主函数
	hello();
	main_menu();
	bye();
	return 0;
}

void hello() {
	//显示欢迎界面
	puts("=================================电 话 簿 管 理 系 统====================================");
	puts("|                                                                                       |");
	puts("|                                                                                       |");
	puts("|                                                                                       |");
	puts("|                                                                                       |");
	puts("|                                                                                       |");
	puts("|                                                                                       |");
	puts("|                                                                                       |");
	puts("|                                                                                       |");
	puts("|                                                                                       |");
	puts("|                                                                                       |");
	puts("|                                                                                       |");
	puts("|                                                                                       |");
	puts("|                                                                                       |");
	puts("|                                                                                       |");
	puts("|                                                                                       |");
	puts("|                                                                                       |");
	puts("|正在加载系统....\t\t\t\t\t\t\t\t\t|");
	puts("=========================================================================================");
	Sleep(2000);//等待2s
}

void display_menu() {//主菜单显示函数
	system("cls");//清屏
	puts("=================================电 话 簿 管 理 系 统====================================");
	puts("|                                                                                       |");
	puts("|选择功能:\t\t\t\t\t\t\t\t\t\t|");
	puts("|                                                                                       |");
	puts("|\t\t1.添加新电话号码\t\t\t\t\t\t\t|");
	puts("|                                                                                       |");
	puts("|\t\t2.搜索电话号码\t\t\t\t\t\t\t\t|");
	puts("|                                                                                       |");
	puts("|\t\t3.删除电话号码\t\t\t\t\t\t\t\t|");
	puts("|                                                                                       |");
	puts("|\t\t4.修改电话信息\t\t\t\t\t\t\t\t|");
	puts("|                                                                                       |");
	puts("|\t\t5.显示电话信息\t\t\t\t\t\t\t\t|");
	puts("|                                                                                       |");
	puts("|\t\t6.排序电话信息\t\t\t\t\t\t\t\t|");
	puts("|                                                                                       |");
	puts("|\t\t0.退出\t\t\t\t\t\t\t\t\t|");
	puts("|                                                                                       |");
	puts("=========================================================================================");
}

void Init() {
	display_menu();//显示主菜单

	FILE* fp;//检测电话存储文件是否存在
	if ((fp = fopen(".\\TelePhone_Number_Dictionary.data", "r")) == NULL) {
		//文件不存在，提示是否创建新文件
		puts("找不到文件:\"TelePhone_Number_Dictionary.data\" !");
		puts("是否创建新文件？(Y/N)");
		char op;
		while (op = getchar()) {//读取用户操作
			if (op == 'y' || op == 'Y' || op == 'n' || op == 'N') {
				if (op == 'y' || op == 'Y') {//如果选择Yes,创建一个新文件
					fp = fopen(".\\TelePhone_Number_Dictionary.data", "w");
					puts("创建成功！");
				}
				else {//否则退出程序
					puts("取消创建！");
					Sleep(1000);
					exit(0);
				}
				break;
			}
		}
	}

	const int LEN = 100;//设置读取长度
	char str[LEN] = { 0 };//初始化字符串
	stu* Tail = Student;//创建双向链表
	Student->next = Student->last = NULL;//初始化双向链表的上一个和下一个节点为空
	Student->name[0] = {0}, Student->number[0] = {0}, Student->remark[0] = {0};//初始化数据
	while (fgets(str, LEN, fp) != NULL) {//读取数据
		stu* Temp = automalloc(stu);//动态申请空间
		Tail->next = Temp;//将链表尾部的下一个节点指向Temp
		Temp->last = Tail;//将temp上一个节点指向链表尾部
		Temp->next = NULL;//Temp下一个节点设为空
		sscanf(str, "%s %s %s", Temp->name, Temp->number, Temp->remark);//读入数据
		Tail = Temp;//将链表尾部更新到Temp
	}
	fclose(fp);//关闭文件读入
	display_menu();//刷新屏幕
	puts("数据加载完成！");//输出数据读取完毕
	Sleep(1000);//等待1s
}

void Save() {//保存数据函数
	display_menu();//显示主界面
	FILE* fp;//打开文件
	if ((fp = fopen(".\\TelePhone_Number_Dictionary.data", "w")) == NULL) {
		puts("文件\"TelePhone_Number_Dictionary.data\"打开失败！");//打开文件失败，报错退出
		system("Pause");
		exit(0);
	}
	else {
		stu* Temp = Student->next;//申请Temp为链表头部
		while (Temp != NULL) {//遍历链表将数据存入文件
			fprintf(fp, "%s %s %s\n", Temp->name, Temp->number, Temp->remark);
			Temp = Temp->next;//遍历链表
		}
		fclose(fp);//关闭文件
		puts("数据保存成功！");//输出信息
		Sleep(1000);//等待1s
	}
}

void main_menu() {//主界面函数
	int op;
	Init();
	while (1) {
		display_menu();//显示主界面
		printf("选择函数：");//显示信息
		scanf("%d", &op);//读入操作数
		switch (op) {//switch选择相应函数
		case 0: return;//退出程序
		case 1: Add(); Save(); break;//添加号码信息
		case 2: Search(); break;//查找号码信息
		case 3: Delete(); Save(); break;//删除号码信息
		case 4: Modify(); Save(); break;//修改号码信息
		case 5: Display(); break;//显示号码信息
		case 6: Sort(); Save(); break;//按照要求排序号码信息
		case 23333: Author(); break;
		}
	}
}

void Add(){//添加号码信息函数
	display_menu();//显示主菜单
	stu* New = automalloc(stu);//申请一个空间存储一会输入的信息
	stu* Temp = Student;//申请一个变量准备遍历链表
	puts("按照顺序输入姓名，电话号码，备注(使用空格隔开):");
	scanf("%s %s %s", New->name, New->number, New->remark);//存储信息

	while (Temp->next != NULL) {//遍历链表
		Temp = Temp->next;//Temp转移到下一个节点
		if (strcmp(Temp->name ,New->name) == 0 && strcmp(Temp->number ,New->number) == 0) {
			//如果有重复名字和电话号码，显示是否更改备注
			puts("存在相同的姓名和备注！");
			printf("当前备注为\"%s\"\n", Temp->remark);//显示当前备注
			puts("你是否要修改当前备注(Y/N)");//输出是否更改备注
			char op;
			while (op = getchar()) {//读取操作
				switch (op) {
				case 'Y':
				case 'y':
					strcpy(Temp->remark, New->remark);
					puts("修改成功！");//修改成功
					Sleep(1000);
					return;
				case 'N':
				case 'n':
					puts("取消修改！");//取消修改
					return;
				}
			}
		}
	}
	//while已经遍历到尾部
	//如果没有重复名字和号码，则添加到尾部
	Temp->next = New;//尾部的下一个节点改为New
	New->next = NULL;//New的下一个节点改为空
	New->last = Temp;//New的上一个节点改为尾部
	puts("添加成功！");//输出添加成功
	Sleep(1000);//等待1s
}

void Search(){//搜索信息函数
	display_menu();
	stu* Temp = Student->next;//获取链表初始节点
	puts("输入姓名、电话号码、备注任意一个以查找:");//输入名字或电话或备注来查找相关信息
	char str[30];
	scanf("%s", str);//读入要搜索的数据
	puts(HEAD); puts(HEAD2);//输出表头
	int cnt = 1;//序号统计
	while (Temp != NULL) {//遍历链表
		if (strcmp(Temp->name, str) == 0 || strcmp(Temp->number, str) == 0 || strcmp(Temp->remark, str) == 0) {
			//如果文件中查找到任意一个匹配的str的信息，就输出此电话信息
			//实现通过任意一个信息(名字，电话，备注）都能查询到所需内容，可以模糊查找
			puts(HEAD3);//输出分割线
			print(cnt, Temp->name, Temp->number, Temp->remark);//使用宏定义简易输出信息
			cnt++;//序号加1
		}
		Temp = Temp->next;//节点移动到下一位
	}
	puts(HEAD);//输出尾部分割线
	puts("搜索完毕！");//输出相关信息
	system("Pause");
}

void Delete() {//删除信息函数
	display_menu();
	stu* Temp = Student->next;
	stu* Del = automalloc(stu);//定义一个存储可能删除的电话信息的链表为Del
	stu* tail = Del;//链表初始化
	Del->last = Del->next = NULL;//链表初始化

	puts("输入姓名、电话号码、备注任意一个以查找:");//任意输入一种名字进行查找
	char str[30];
	scanf("%s", str);//读取输入
	puts(HEAD); puts(HEAD2);//输出表头
	int cnt = 1;
	while (Temp != NULL) {//开始遍历原数据的链表
		//找出模糊匹配出的数据
		if (strcmp(Temp->name, str) == 0 || strcmp(Temp->number, str) == 0 || strcmp(Temp->remark, str) == 0) {
			puts(HEAD3);//输出分割线
			print(cnt, Temp->name, Temp->number, Temp->remark);//输出电话信息
			stu* p = automalloc(stu);//存储到Del链表
			strcpy(p->name, Temp->name);
			strcpy(p->number, Temp->number);
			strcpy(p->remark, Temp->remark);
			p->last = tail;
			p->next = NULL;
			tail->next = p;
			tail = p;//链表添加数据完成
			cnt++;//序号+1
		}
		Temp = Temp->next;
	}
	puts(HEAD);
	puts("搜索完毕！");//输出搜索完毕
	if (cnt == 1) {//如果序号没变，说明没找到数据
		puts("未找到相关信息！");//输出无数据
		Sleep(2000);
		return;//结束删除
	}
	puts("选择要删除的信息(输入序号):");//选择要删除的信息
	int num;
	scanf("%d", &num);
	Temp = Del;
	for (int i = 0; i < num; ++i) Temp = Temp->next;//遍历直到删除的位置
	stu temp;//使用temp存储要删的数据
	strcpy(temp.name, Temp->name);//复制数据
	strcpy(temp.number, Temp->number);
	strcpy(temp.remark, Temp->remark);
	Temp = Del;//销毁Del链表
	while (Temp != NULL) {
		stu* nextTemp = Temp->next;//保存下一位数据的地址
		free(Temp);//销毁空间
		Temp = nextTemp;//移动到下一位数据
	}
	Del = NULL;//销毁Del指针
	Temp = Student->next;//从原数据链表开始查找
	while (Temp != NULL) {
		if (strcmp(Temp->name, temp.name) == 0 && strcmp(Temp->number, temp.number) == 0 && strcmp(Temp->remark, temp.remark) == 0) {
			//三个信息都和temp匹配，则删除这个数据
			Temp = Temp->last;//指针指向位置进行更改
			Temp->next = Temp->next->next;
			if(Temp->next != NULL) Temp->next->last = Temp;
			break;
		}
		Temp = Temp->next;
	}
	puts("删除成功！");
	system("Pause");
}

void Modify() {//修改函数
	display_menu();
	stu* Temp = Student->next;//同删除函数，申请一个Temp指针进行遍历链表
	stu* Del = automalloc(stu);//申请一个Del函数显示可能删除的信息
	stu* tail = Del;
	Del->last = Del->next = NULL;

	puts("输入姓名、电话号码、备注任意一个以查找:");
	char str[30], change[30];
	scanf("%s", str);
	puts(HEAD); puts(HEAD2);
	int cnt = 1;
	while (Temp != NULL) {//进行模糊匹配可能删除的信息，与删除函数相同
		if (strcmp(Temp->name, str) == 0 || strcmp(Temp->number, str) == 0 || strcmp(Temp->remark, str) == 0) {
			puts(HEAD3);
			print(cnt, Temp->name, Temp->number, Temp->remark);
			stu* p = automalloc(stu);
			strcpy(p->name, Temp->name);
			strcpy(p->number, Temp->number);
			strcpy(p->remark, Temp->remark);
			p->last = tail;
			p->next = NULL;
			tail->next = p;
			tail = p;
			cnt++;
		}
		Temp = Temp->next;
	}
	puts(HEAD);
	puts("搜索完毕！");
	if (cnt == 1) {//未找到相关信息
		puts("未找到相关信息！");
		Sleep(2000);
		return;
	}
	//找到相关信息，进行选择操作
	puts("选择要修改的信息(输入序号):");
	int num;
	scanf("%d", &num);
	getchar();
	//选择要修改的信息
	puts("选择要修改的信息类型(输入字母)(姓名: a\\电话: b\\备注: c):");
	int flag = 0;
	char op;
	while (op = getchar()) {//读取选择的结果
		if (op == 'a' || op == 'A' || op == 'b' || op == 'B' || op == 'c' || op == 'C') {
			switch (op) {
			case 'a':
			case 'A':
				flag = 1; break;
			case 'B':
			case 'b':
				flag = 2; break;
			case 'C':
			case 'c':
				flag = 3; break;
			}
			break;
		}
	}
	Temp = Del;//找到要删除的信息
	for (int i = 0; i < num; ++i) Temp = Temp->next;
	stu temp;//用结构体临时保存信息
	strcpy(temp.name, Temp->name);
	strcpy(temp.number, Temp->number);
	strcpy(temp.remark, Temp->remark);
	//将要改变的信息展示出来
	if (flag == 1) strcpy(str, Temp->name);
	else if (flag == 2) strcpy(str, Temp->number);
	else if (flag == 3) strcpy(str, Temp->remark);
	printf("你想要修改\"%s\"为:", str);
	//读取更改后的信息
	scanf("%s", change);
	Temp = Del;//销毁链表
	while (Temp != NULL) {
		stu* nextTemp = Temp->next;
		free(Temp);
		Temp = nextTemp;
	}
	Del = NULL;
	Temp = Student->next;
	while (Temp != NULL) {//找到需要更改的信息
		if (strcmp(Temp->name, temp.name) == 0 && strcmp(Temp->number, temp.number) == 0 && strcmp(Temp->remark, temp.remark) == 0) {
			if (flag == 1) strcpy(Temp->name, change);//进行更改操作
			else if (flag == 2) strcpy(Temp->number, change);
			else if (flag == 3) strcpy(Temp->remark, change);
			break;//更改完毕后退出
		}
		Temp = Temp->next;
	}
	puts("修改成功！");
	system("Pause");
}

void Display() {//展示所有信息
	display_menu();
	stu* Temp = Student->next;//初始化指针
	puts(HEAD); puts(HEAD2);//输出表头
	int cnt = 1;//序号统计
	while (Temp != NULL) {//输出数据
		puts(HEAD3);//分行
		print(cnt, Temp->name, Temp->number, Temp->remark);
		cnt++;
		Temp = Temp->next;
	}
	puts(HEAD);//输出表格最后一行
	puts("展示完成！");
	system("Pause");
}

void Author() {//Author Information
	system("cls");
	puts("=================================电 话 簿 管 理 系 统====================================");
	puts("|                                                                                       |");
	puts("|YOU FFFFFFFFFFFFFFIND Easter egg !\t\t\t\t\t\t\t|");
	puts("|                                                                                       |");
	puts("|Author: chen_wan_qing\t\t\t\t\t\t\t\t\t|");
	puts("|Designer: chen_wan_qing\t\t\t\t\t\t\t\t|");
	puts("|Completion date: 2022/06/01\t\t\t\t\t\t\t\t|");
	puts("|                                                                                       |");
	puts("|Thanks for using !\t\t\t\t\t\t\t\t\t|");
	puts("=========================================================================================");
	system("Pause");
}

void swap(stu * first, stu * second) {//自定义交换函数，将第一个结构体与第二个结构体交换数据
	stu temp;
	strcpy(temp.name, first->name);
	strcpy(temp.number, first->number);
	strcpy(temp.remark, first->remark);
	strcpy(first->name, second->name);
	strcpy(first->number, second->number);
	strcpy(first->remark, second->remark);
	strcpy(second->name, temp.name);
	strcpy(second->number, temp.number);
	strcpy(second->remark, temp.remark);
}

void Sort() {//排序函数
	display_menu();
	stu* Temp = Student->next;//初始化指针
	//选择按照什么顺序排字典序
	puts("选择要排序类型(姓名: a\\电话号码: b\\备注: c): ");
	int flag;
	char op;
	while (op = getchar()) {//读取选择的结果
		if (op == 'a' || op == 'A' || op == 'b' || op == 'B' || op == 'c' || op == 'C') {
			switch (op) {
			case 'a':
			case 'A':
				flag = 1; break;
			case 'B':
			case 'b':
				flag = 2; break;
			case 'C':
			case 'c':
				flag = 3; break;
			}
			break;
		}
	}
	//按照选择结果进行冒泡排序，以下三个if同理
	if(flag == 1){
		stu* Temp2;
		while (Temp != NULL) {//冒泡排序
			Temp2 = Temp->next;
			while (Temp2 != NULL) {
				if (strcmp(Temp->name, Temp2->name) > 0)
					swap(Temp, Temp2);
				Temp2 = Temp2->next;
			}
			Temp = Temp->next;
		}
	}
	else if (flag == 2) {
		stu* Temp2;
		while (Temp != NULL) {
			Temp2 = Temp->next;
			while (Temp2 != NULL) {
				if (strcmp(Temp->number, Temp2->number) > 0)
					swap(Temp, Temp2);
				Temp2 = Temp2->next;
			}
			Temp = Temp->next;
		}
	}
	else if (flag == 3) {
		stu* Temp2;
		while (Temp != NULL) {
			Temp2 = Temp->next;
			while (Temp2 != NULL) {
				if (strcmp(Temp->remark, Temp2->remark) > 0)
					swap(Temp, Temp2);
				Temp2 = Temp2->next;
			}
			Temp = Temp->next;
		}
	}
	puts("排序完成！");
	Sleep(1000);
	return;
}

void bye() {//结束程序函数
	system("cls");
	puts("=================================电 话 簿 管 理 系 统====================================");
	puts("");
	puts("");
	puts("\t======              \\\\              //          ================");
	puts("\t||    ==             \\\\            //           ||");
	puts("\t||      ==            \\\\          //            ||");
	puts("\t||       ||            \\\\        //             ||");
	puts("\t||       ||             \\\\      //              ||");
	puts("\t||    ===                \\\\    //               ||                 _---_");
	puts("\t||===                     \\\\  //                ||==========      /     \\");
	puts("\t||   ==                     ||                  ||                        \\   _-");
	puts("\t||     ==                   ||                  ||                          --");
	puts("\t||       ||                 ||                  ||");
	puts("\t||    ===                   ||                  ||");
	puts("\t||===                       ||                  ================");
	puts("");
	puts("");
	puts("=========================================================================================");
	Sleep(2000);
}